Apple, the Apple logo, and Macintosh are registered trademarks of Apple Computer, Inc.
Mac and OpenDoc are trademarks of Apple Computer, Inc.
What it Is
The CPrinter class provides basic support for printing OpenDoc frames. It's pretty simple-minded and doesn't support fancy features like frame-per-page printing (as described in the Printing recipe.) It simply takes the frame you give it, tiles it into page-sized rectangles, and prints each one on a page. It does, however, support both QuickDraw and QuickDraw GX printing, which is essential if your part is to print properly (i.e. not crash!) on a machine with GX installed.
This code should not be treated as holy writ! If it doesn't do what you want (see above) you are welcome to add functionality to it. You could even just study it to determine how it works (particularly the OpenDoc related code) and then write your own printing code.
Features
• Supports both the classic (QuickDraw) and GX printing managers. GX users get the neat-o new printing dialogs.
• Page setups is stored persistently (in the kODPropPageSetup property of the part.)
• E-Z API — just add four simple calls to your code. It's much easier than supporting printing in an app!
The API
Here's the public part of the API, from <Printer.h>:
void PrintDocument( Environment*, ODFrame* initiator, ODShape* area =kODNULL );
};
How To Use It
First off, you must link the printing code into your editor by adding the source files Printer.cpp, PrinterQD.cpp and PrinterGX.cpp to your project/makefile.
• You must be using the Except utility and be prepared to catch exceptions thrown by the printing code.
• You will need to link against the QuickDrawGXLib shared library. Make this a "weak" import unless your part already requires GX in order to run. See your development system documentation for details on how to weak-import a CFM shared library.
The utility consists of a single C++ class called CPrinter. Each instance of your part should have its own CPrinter object. The natural way to do this is to add an instance variable of type “CPrinter*” to your part class or its C++ wrapper. Here is a list of which methods of your part need to talk to the printer object:
In InitPart and InitPartFromStorage
Create a new CPrinter object by calling the factory method, and attach it to your part:
fPrinter = CPrinter::New(ev, storageUnit);
(You can't just say “new CPrinter” because you need to instantiate a different type of printer — a different subclass — depending on whether or not GX is installed.)
In Externalize
Unless the draft is read-only, call the Externalize method of the printer object to update the page-setup property of the part's storage unit, if the user has changed the page setup. If you keep a separate flag to determine whether or not your content is dirty and needs to be saved, you should ignore this flag and always call Externalize on the printer anyway, since the page setup is not part of your content and might change independently. There's no need to externalize your entire content just because the page setup changed!
Again, tell the printer object to Externalize — passing in the target storage unit — so the page setup gets cloned.
fPrinter->Externalize(ev,targetStorageUnit);
In AdjustMenus
Don't forget to enable the Page Setup and Print commands when you're the root part. (You need to talk to the currently installed menu bar, not your own menu bar, since you might not have the menu focus; you're just being called because you're the root part.)
Handle the Page Setup and Print commands. In response to Page Setup, call the printer object's PageSetup method. If it returns true, the page setup has been changed and you should call SetChangedFromPrev on the draft to mark it as changed. If you have a private "dirty" flag for your content, you don't need to set it, since the page setup is not part of your content. There's no need to externalize your entire content just because the page setup changed!
case kODCommandPageSetup:
if( fPrinter->PageSetup(ev) )
ODGetDraft(ev,somSelf)->SetChangedFromPrev(ev);
break;
case kODCommandPrint:
fPrinter->PrintDocument(ev,frame); // Use the frame passed to HandleEvent.
break;
In ReleaseAll
Don't forget to delete the printer object.
ODDeleteObject(fPrinter);
(The printer object acquires a reference to the part's storage unit, so it's best to delete the printer object in the ReleaseAll method and not wait 'til the part's destructor is called.)
Acknowlegements & Bibliography
The original printing code was written by Steve Smith. Thomas Weisbach added QuickDraw GX support. Jens Alfke cleaned up the class structure and API, made the code more robust, and wrote the documentation. All of us paid close attention to the following:
Macintosh Tech Note PR10 A Printing Loop That Cares.